﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Hiro.Interfaces;

namespace Hiro
{
    /// <summary>
    /// Represents a dependency graph that describes all the dependencies in a given application.
    /// </summary>
    public class DependencyMap : IDependencyMap
    {
        /// <summary>
        /// The list of entries that map the activation points to their respective dependencies.
        /// </summary>
        private readonly Dictionary<IDependency, IActivationPoint> _entries = new Dictionary<IDependency, IActivationPoint>();

        /// <summary>
        /// Gets a value indicating the list of services that can be instantiated by the compiled container
        /// using the current list of available activation points.
        /// </summary>
        /// <value>The list of services that will be instantiated by the container once it has been compiled.</value>
        public IEnumerable<IService> AvailableServices
        {
            get
            {
                var results = new List<IService>();
                foreach (var entry in _entries)
                {
                    var activationPoint = entry.Value;
                    if (!activationPoint.IsValidWith(this))
                        continue;

                    var service = new Service(entry.Key, activationPoint);
                    results.Add(service);
                }

                return results;
            }
        }

        /// <summary>
        /// Adds a dependency to the current dependency map and uses the given <paramref name="activationPoint"/>
        /// to realize that dependency.
        /// </summary>
        /// <param name="dependency">The dependency that describes the service to be created.</param>
        /// <param name="activationPoint">The implementation that will instantiate the dependency itself.</param>
        public void AddService(IDependency dependency, IActivationPoint activationPoint)
        {
            _entries[dependency] = activationPoint;
        }

        /// <summary>
        /// Determines whether or not a particular service dependency exists in the current dependency map.
        /// </summary>
        /// <param name="dependency">The target service dependency.</param>
        /// <returns><c>true</c> if the service exists; otherwise, it will return <c>false</c>.</returns>
        public bool Contains(IDependency dependency)
        {
            return _entries.ContainsKey(dependency);
        }

        /// <summary>
        /// Obtains the activation point that will instantiate the given <paramref name="dependency"/>.
        /// </summary>
        /// <param name="dependency">The target service dependency that needs to be activated.</param>
        /// <returns>An <see cref="IActivationPoint"/> instance that will instantiate the service described by the dependency.</returns>
        public IActivationPoint GetActivationPoint(IDependency dependency)
        {
            return _entries[dependency];
        }

        /// <summary>
        /// Obtains the activation point that will instantiate the given <paramref name="dependency"/>.
        /// </summary>
        /// <param name="dependency">The target service dependency that needs to be activated.</param>
        /// <returns>An <see cref="IActivationPoint"/> instance that will instantiate the service described by the dependency.</returns>
        public bool CanResolve(IDependency dependency)
        {
            // The activation point must exist
            if (!Contains(dependency))
                return false;

            var activationPoint = GetActivationPoint(dependency);
            return activationPoint != null && activationPoint.IsValidWith(this);
        }
    }
}
